<?php

/**
 * use two step structure to store template
 * step 1. template: name, [header: name, footer:name, content: name]
 * step 2. header: name, elements: []
 * step 2. footer: name, elements: []
 * step 2. content: name, elements: []
 */
class Template
{
	/**
	 * @var array
	 */
	protected $_headVars = array();

	public $name = null;

	public $theme = 'default';

	// execute a template name
	public function execute($name = null)
	{

		$layouts = array();

		$layoutConfig = array(
			'header',
			'content',
			'footer'
		);

		foreach ($layoutConfig as $layoutName)
		{
			$layouts[$layoutName] = $this -> _executeLayout($layoutName, $name);
		}

		$xhtml = implode('<div class="clear-fix"></div>', $layouts);

		$page = '<html>';
		$page .= '<head>';

		$page .= $this -> _executeHead();

		$page .= '</head>';
		$page .= '<body><div id="site">' . $xhtml . '</div></body>';
		$page .= '</html>';
		return $page;

	}

	protected function _executeLayout($layoutName, $layoutId)
	{
		$xhtml = array();

		$layout = $this -> loadLayout($layoutName, $layoutId);

		$locations = $layout['locations'];

		//each region need to work as controller and some thing else.

		foreach ($locations as $location => $elements)
		{
			$xhtml[$location] = $this -> _executeLocation($location, $elements);
		}

		$render = isset($layout['layout']) ? $layout['layout'] : 'layout1';

		$render = 'Render_' . ucfirst($render);

		$render = new $render;

		$xhtml = $render -> execute($xhtml);

		$xhtml = '<div class="lay-' . $layoutName . '">' . $xhtml . '</div>';

		return $xhtml;
	}

	public function _executeLocation($location, $elements)
	{
		$xhtml = '';

		foreach ($elements as $element)
		{
			// execute each region to make sure.
			try
			{
				$controller = $element['controller'];

				$params = $element['params'];

				$cls = str_replace('/', '-', $controller);

				$xhtml .= '<div class="' . $cls . '">' . Request::factory($controller, $params) -> execute() -> response . '</div>';

			}
			catch(Exception $e)
			{
				if (APPLICATION_ENV == 'development')
				{
					echo $e -> getMessage();
				}
			}
		}
		return '<div class="loc-' . $location . '">' . $xhtml . '</div>';
	}

	/**
	 * load layout by name and id.
	 * @param string $layoutName   Is used for default layout id value.
	 * @param string/id $layoutId  Is used to search layout configure in a
	 * MongoDb Table when has a custom configured.
	 */
	public function loadLayout($layout, $name)
	{
		if ($name == null)
		{
			$name = $this -> name;
		}

		if (null == $name)
		{
			$name = Request::initiator() -> getController();
		}

		$id = sprintf('%s-%s', $layout, $name);

		$db = Mongo_Database::instance() -> selectCollection('layouts');

		$result = $db -> findOne(array('_id' => $id));

		if (null == $result)
		{
			$id = sprintf('%s-%s', $layout, 'default');
			$result = $db -> findOne(array('_id' => $id));
		}

		if (null == $result)
		{

			$id = sprintf('%s-%s', $layout, 'default');

			$result = $db -> findOne(array('_id' => $id));
		}

		if (null == $result)
		{
			switch($layout) {
				case 'header' :
				case 'footer' :
					$result = array('locations' => array());
					break;
				case 'content' :
					$result = array('locations' => array('content' => array( array(
									'controller' => 'block/content',
									'params' => array()
								))));
			}
		}
		return $result;
	}

	/**
	 * @return string
	 */
	public function _executeHead()
	{

		$this -> _config = Agilet::getSetting('general');

		$theme = $this->theme;

		$staticUrl = Agilet::getStaticUrl();

		$appUrl = Agilet::getAppUrl();

		/**
		 * process some thing else as content or not of page.
		 */
		$this -> addMeta(array(
			'http-equiv' => 'content-type',
			'content' => 'text/html; charset=utf-8'
		)) -> addMeta(array(
			'http-equiv' => 'content-language',
			'content' => 'en-us',
		)) -> addMeta(array(
			'name' => 'revisit-after',
			'content' => $this -> getConfig('revisit-after', '1 days'),
		)) -> addMeta(array(
			'name' => 'robots',
			'content' => $this -> getConfig('robots', 'robots.txt')
		)) -> addMeta(array(
			'name' => 'owner',
			'content' => $this -> getConfig('owner')
		)) -> addMeta(array(
			'name' => 'author',
			'content' => $this -> getConfig('author')
		)) -> addMeta(array(
			'name' => 'generator',
			'content' => $this -> getConfig('generator')
		)) -> addLink(array(
			'rel' => 'shortcut icon',
			'type' => 'image/x-icon',
			'href' => $staticUrl . '/favicon.ico'
		));

		$this -> prependCssLink($staticUrl . '/minify/index.php?g=require.css');
		$this -> prependCssLink($staticUrl . '/' . $theme . '/styles/main.css');
		$this -> prependJsLink($staticUrl . '/minify/index.php?g=require.js&lang=vi');
		$this -> prependJsLink($staticUrl . '/minify/index.php?g=main.js'); ;

		/**
		 * @var array
		 */
		$allows = array(
			'Base',
			'Link',
			'CssLink',
			'Meta',
			'Keyword',
			'Description',
			'Title',
			'Css',
			'JsLink',
			'Script'
		);

		/**
		 * @var array
		 */
		$xhtml = array();

		foreach ($allows as $allow)
		{
			if (($html = $this -> {'_renderHead' . $allow}()) != '')
			{
				$xhtml[] = $html;
			}
		}
		return implode(PHP_EOL, $xhtml);
	}

	public function addCssLink($value)
	{
		return $this -> __processHead('add', 'CssLink', $value);
	}

	public function appendCssLink($value)
	{
		return $this -> __processHead('append', 'CssLink', $value);
	}

	public function prependCssLink($value)
	{
		return $this -> __processHead('prepend', 'CssLink', $value);
	}

	public function addJsLink($value)
	{
		return $this -> __processHead('add', 'JsLink', $value);
	}

	public function appendJsLink($value)
	{
		return $this -> __processHead('append', 'JsLink', $value);
	}

	public function prependJsLink($value)
	{
		return $this -> __processHead('prepend', 'JsLink', $value);
	}

	public function addLink($value)
	{
		return $this -> __processHead('add', 'Link', $value);
	}

	public function appendLink($value)
	{
		return $this -> __processHead('append', 'Link', $value);
	}

	public function prependLink($value)
	{
		return $this -> __processHead('prepend', 'Link', $value);
	}

	public function addScript($value)
	{
		return $this -> __processHead('add', 'Script', $value);
	}

	public function appendScript($value)
	{
		return $this -> __processHead('append', 'Script', $value);
	}

	public function prependScript($value)
	{
		return $this -> __processHead('prepend', 'Script', $value);
	}

	public function addMeta($value)
	{
		$this -> _headVars['Meta'][] = $value;
		return $this;
	}

	public function appendMeta($value)
	{
		return $this -> __processHead('append', 'Meta', $value);
	}

	public function prependMeta($value)
	{
		return $this -> __processHead('prepend', 'Meta', $value);
	}

	public function addTitle($value)
	{
		return $this -> __processHead('add', 'Title', $value);
	}

	public function appendTitle($value)
	{
		return $this -> __processHead('append', 'Title', $value);
	}

	public function prependTitle($value)
	{
		return $this -> __processHead('prepend', 'Title', $value);
	}

	public function addKeyword($value)
	{
		return $this -> __processHead('add', 'Keyword', $value);
	}

	public function appendKeyword($value)
	{
		return $this -> __processHead('append', 'Keyword', $value);
	}

	public function prependKeyword($value)
	{
		return $this -> __processHead('prepend', 'Keyword', $value);
	}

	public function addDescription($value)
	{
		return $this -> __processHead('add', 'Description', $value);
	}

	public function appendDescription($value)
	{
		return $this -> __processHead('append', 'Description', $value);
	}

	public function prependDescription($value)
	{
		return $this -> __processHead('prepend', 'Description', $value);
	}

	/**
	 * placement
	 * @param string $name
	 * @param string/array $value
	 * @return mixed
	 */
	protected function __processHead($action, $key, $value = null)
	{

		if (!isset($this -> _headVars[$key]))
		{
			$this -> _headVars[$key] = array();
		}

		if (method_exists($this, $method = '_correct' . $key))
		{
			$value = $this -> {$method}($value);
		}

		switch($action) {
			case 'append' :
			case 'add' :
				$this -> _headVars[$key][] = $value;
				return $this;
			case 'prepend' :
				$this -> _headVars[$key] = array_reverse($this -> _headVars[$key]);
				$this -> _headVars[$key][] = $value;
				$this -> _headVars[$key] = array_reverse($this -> _headVars[$key]);
				return $this;
			case 'clear' :
				$this -> _headVars[$key] = array();
				return $this;
			case 'set' :
				$this -> _headVars[$key] = array($value);
			case 'has' :
				return isset($this -> _headVars[$key]) && !empty($this -> _headVars[$key]);
			case 'none' :
		}
		return $this;
	}

	/**
	 * match action
	 * @param string $name
	 * @param string $option
	 * @param string $default
	 */
	protected function __empty($name, $option, $default = '')
	{

		$value = $this -> getConfig($option, $default = '');

		if (isset($this -> _vars[$name]) && !empty($this -> _vars[$name]))
		{
			$this -> process($this -> getConfig($option . '-option', ' append'), $name, $value);
		}
		else
		{
			$this -> process('set', $name, $value);
		}
		return $this;
	}

	static protected function __flat($array)
	{
		$xhtml = array();
		if (is_array($array))
		{
			foreach ($array as $key => $value)
			{
				$xhtml[] = sprintf('%s="%s"', $key, $value);
			}
		}

		return implode(' ', $xhtml);
	}

	/**
	 * get html from current tile
	 * @return
	 */
	protected function _renderHeadTitle()
	{
		$vars = $this -> getHeadVar('Title');

		$sep = $this -> getConfig('title-separate', '-');

		if (empty($vars))
		{
			$vars = array($this -> getConfig('title'));
		}

		return sprintf('<title>%s</title>', implode($sep, $vars));
	}

	/**
	 * flatten meta to html
	 * @return string
	 */
	protected function _renderHeadMeta()
	{
		$xhtml = array();

		foreach ($this->getHeadVar('Meta') as $meta)
		{
			$xhtml[] = '<meta ' . $this -> __flat($meta) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten keyword to html
	 * @return string
	 */
	protected function _renderHeadKeyword()
	{
		$vars = $this -> getHeadVar('Keyword');
		if (empty($keyword))
		{
			$vars = array($this -> getConfig('keyword'));
		}
		return '<meta name="keyword" content="' . implode(', ', $vars) . '"/>';
	}

	/**
	 * flatten description to html
	 * @return string
	 */
	protected function _renderHeadDescription()
	{
		$vars = $this -> getHeadVar('Description');
		if (empty($keyword))
		{
			$vars = array($this -> getConfig('description'));
		}
		return '<meta name="description" content="' . implode(', ', $vars) . '"/>';
	}

	/**
	 * flatten link to html
	 * @return string
	 */
	protected function _renderHeadLink()
	{
		$xhtml = array();

		foreach ($this->getHeadVar('Link') as $value)
		{
			$xhtml[] = '<link ' . $this -> __flat($value) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten css link to html
	 * @return string
	 */
	protected function _renderHeadCssLink()
	{
		$xhtml = array();

		foreach ($this->getHeadVar('CssLink') as $value)
		{
			$xhtml[] = '<link ' . $this -> __flat($value) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten js link to html
	 * @return string
	 */
	protected function _renderHeadJsLink()
	{
		$xhtml = array();
		foreach ($this->getHeadVar('JsLink') as $value)
		{
			$xhtml[] = '<script ' . $this -> __flat($value) . '></script>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * get base href tags
	 * @return string
	 */
	protected function _renderHeadBase()
	{
		return '<base href="http://www.k1.vn"/>';
	}

	/**
	 * flatten opengrap to meta, it is helpful for social sites to know what is
	 * this
	 * site
	 * @return string
	 */
	protected function _renderHeadOpengraph()
	{
		return '';
	}

	protected function _renderHeadScript()
	{
		$script =  implode(';'. PHP_EOL ,$this->getHeadVar('Script'));
		
		if($script){
			return '<script type="text/javascript">' . $script . '</script>';
		}
		
		return '';
	}

	protected function _renderHeadCss()
	{
		$style =  implode(';'. PHP_EOL ,$this->getHeadVar('Css'));
		
		if($style){
			return '<style type="text/css">' . $style . '</style>';
		}
		
		return '';
	}

	protected function _correctCssLink($value)
	{
		if (is_string($value))
		{
			$value = array('href' => $value);
		}
		return array_merge(array(
			'type' => 'text/css',
			'rel' => 'stylesheet',
			'media' => 'all'
		), $value);
	}

	protected function _correctJsLink($value)
	{
		if (is_string($value))
		{
			$value = array('src' => $value);
		}
		return array_merge(array('type' => 'text/javascript'), $value);
	}/**

	 * @param string $name
	 * @param mixed $default
	 * @return mixed
	 */
	public function getConfig($key, $default = null)
	{
		return isset($this -> _config[$key]) ? $this -> _config[$key] : $default;
	}

	/**
	 * @param array $options
	 * @return
	 */
	public function setConfig($key, $value)
	{
		$this -> _config[$key] = $value;
		return $this;
	}

	/**
	 * @param string $name
	 * @return array
	 */
	public function getHeadVar($key)
	{
		return isset($this -> _headVars[$key]) ? $this -> _headVars[$key] : array();
	}

}
